﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using Random = System.Random;

namespace Extend.RandomTool
{
    /// <summary>
    /// 权重对象
    /// </summary>
    public abstract class IRandomObject
    {
        /// <summary>
        /// 权重
        /// </summary>
        public int Weight;
    }

    public class RandomHelper
    {
        /// <summary>
        /// 算法：
        /// 1.每个广告项权重+1命名为w，防止为0情况。
        /// 2.计算出总权重n。
        /// 3.每个广告项权重w加上从0到(n-1)的一个随机数（即总权重以内的随机数），得到新的权重排序值s。
        /// 4.根据得到新的权重排序值s进行排序，取前面s最大几个。
        /// 
        /// 此算法会将小概率事件概率放大
        /// </summary>
        /// <param name="list">原始列表</param>
        /// <param name="count">随机抽取条数</param>
        /// <returns></returns>
        public static List<T> GetRandomList<T>(List<T> list, int count) where T : IRandomObject
        {
            if (list == null || list.Count <= count || count <= 0)
            {
                return list;
            }

            //计算权重总和
            int totalWeights = 0;
            for (int i = 0; i < list.Count; i++)
            {
                totalWeights += list[i].Weight + 1;  //权重+1，防止为0情况。
            }

            //随机赋值权重
            Random ran = new Random(GetRandomSeed());  //GetRandomSeed()随机种子，防止快速频繁调用导致随机一样的问题 
            List<KeyValuePair<int, int>> wlist = new List<KeyValuePair<int, int>>();    //第一个int为list下标索引、第一个int为权重排序值
            for (int i = 0; i < list.Count; i++)
            {
                int w = (list[i].Weight + 1) + ran.Next(0, totalWeights);   // （权重+1） + 从0到（总权重-1）的随机数
                wlist.Add(new KeyValuePair<int, int>(i, w));
            }

            //排序
            wlist.Sort(
              delegate (KeyValuePair<int, int> kvp1, KeyValuePair<int, int> kvp2)
              {
                  return kvp2.Value - kvp1.Value;
              });

            //根据实际情况取排在最前面的几个
            List<T> newList = new List<T>();
            for (int i = 0; i < count; i++)
            {
                T entiy = list[wlist[i].Key];
                newList.Add(entiy);
            }

            //随机法则
            return newList;
        }

        /// <summary>
        /// 随机种子值
        /// 为了防止在短时间内频繁创建和调用Random时候，会出现重复的随机数（也就是随机不在是随机的问题）
        /// </summary>
        /// <returns></returns>
        public static int GetRandomSeed()
        {
            byte[] bytes = new byte[4];
            System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
            rng.GetBytes(bytes);
            return BitConverter.ToInt32(bytes, 0);
        }

        /// <summary>
        /// 此算法会将小概率事件概率放大
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public static List<int> GetRandomList(List<int> list) 
        {
            if (list == null || list.Count <= 0)
            {
                return list;
            }

            int commonWeight = 1;

            //计算权重总和
            int totalWeights = 0;
            for (int i = 0; i < list.Count; i++)
            {
                totalWeights += commonWeight + 1;  //权重+1，防止为0情况。
            }

            //随机赋值权重
            Random ran = new Random(GetRandomSeed());  //GetRandomSeed()随机种子，防止快速频繁调用导致随机一样的问题 
            List<KeyValuePair<int, int>> wlist = new List<KeyValuePair<int, int>>();    //第一个int为list下标索引、第一个int为权重排序值
            for (int i = 0; i < list.Count; i++)
            {
                int w = (commonWeight + 1) + ran.Next(0, totalWeights);   // （权重+1） + 从0到（总权重-1）的随机数
                wlist.Add(new KeyValuePair<int, int>(i, w));
            }

            //排序
            wlist.Sort(
              delegate (KeyValuePair<int, int> kvp1, KeyValuePair<int, int> kvp2)
              {
                  return kvp2.Value - kvp1.Value;
              });

            //根据实际情况取排在最前面的几个
            List<int> newList = new List<int>();
            for (int i = 0; i < list.Count; i++)
            {
                int entiy = list[wlist[i].Key];
                newList.Add(entiy);
            }

            //随机法则
            return newList;
        }

        /// <summary>
        /// 保证表现概率与实际概率一致
        /// </summary>
        public static List<T> GetRandomList_TrueRatio<T>(List<T> randomlist) where T : IRandomObject
        {
            List<T> list = new List<T>(randomlist);
            int sum = 0;
            for (int i = 0; i < list.Count; i++)
            {
                sum += list[i].Weight;
            }
            for (int i = 0; i < list.Count; i++)
            {
                int ran = UnityEngine.Random.Range(0, sum);
                for (int j = i; j < list.Count; j++)
                {
                    if (ran < list[j].Weight)
                    {
                        if (i != j)
                        {
                            var tmp = list[i];
                            list[i] = list[j];
                            list[j] = tmp;
                        }
                        break;
                    }
                    ran -= list[j].Weight;
                }
                sum -= list[i].Weight;
            }
            return list;
        }
        /// <summary>
        /// 保证表现概率与实际概率一致
        /// </summary>
        public static T GetRandomOne_TrueRatio<T>(List<T> randomlist) where T : IRandomObject
        {
            int sum = 0;
            for (int i = 0; i < randomlist.Count; i++)
            {
                sum += randomlist[i].Weight;
            }
            int ran = UnityEngine.Random.Range(0, sum);

            for (int i = 0; i < randomlist.Count; i++)
            {
                if (ran < randomlist[i].Weight)
                {
                    return randomlist[i];
                }
                ran -= randomlist[i].Weight;
            }
            return randomlist[randomlist.Count - 1];
        }
    }
}
